home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2000 / MacHack 2000.toast / pc / The Hacks / Genie / Projects / AEGizmos 1.4.2 / Source / Sources / AEBuild.c next >
Encoding:
C/C++ Source or Header  |  2000-06-24  |  21.4 KB  |  854 lines

  1. /*
  2.  *    AppleEvent Builder Function
  3.  *    by Jens Peter Alfke
  4.  *
  5.  *    Copyright ©1991-1993 Apple Computer, Inc. All rights reserved.
  6.  *
  7.  *    APPLE CONFIDENTIAL
  8.  */
  9.  
  10.  
  11. /*
  12.     CHANGE HISTORY:
  13.      ...zillions of changes from 2/91 until...
  14.      6/30/93    jpa        Fixed AEBuildAppleEvent; wrong arg passed to va_start.
  15.     10/11/93    jpa        Fix '\r' reference for MPW C.
  16.      4/14/94    jpa        Optimize & fix warnings for Metrowerks.
  17.                          Disable 'exte' descriptors on PowerPC.
  18.      1/27/95    jpa        Fixed generation of 'bool' descriptors (1 byte not 2).
  19.      3/??/95    jpa        Fixed ctype macros for CodeWarrior: cast to unsigned char
  20.      3/20/94    jpa        **AEGizmos 1.4
  21. */
  22.  
  23.  
  24. #ifndef __POWERPC__
  25. #include <SANE.h>
  26. #endif
  27. #include <Errors.h>
  28. #include <stdarg.h>
  29. #include "AEStream.h"
  30. #include "AEBuild.h"
  31.  
  32. #ifdef __MWERKS__
  33.     // Metrowerks CodeWarrior's <ctype> functions are exactly what we need.
  34.     #include <ctype.h>
  35.     #define ISSPACE(C)        isspace((unsigned char)C)
  36.     #define ISGRAPH(C)        isgraph((unsigned char)C)
  37.     #define ISDIGIT(C)        isdigit((unsigned char)C)
  38.     #define ISXDIGIT(C)        isxdigit((unsigned char)C)
  39. #elif defined(THINK_C)
  40.     // THINK C's <ctype> functions are pretty much dandy, except that all >127 (option-) characters
  41.     // are considered nonspaces and nongraphical. So we make them all graphical except for A0
  42.     // (option-space) which is a space:
  43.     
  44.     #include <ctype.h>
  45.     #define ISSPACE(C)        (isspace(C) || ((uchar)(C))==0xCA)
  46.     #define ISGRAPH(C)        (isgraph(C) || ((uchar)(C))>127 && (uchar)(C)!=0xCA)
  47.     #define ISDIGIT(C)         isdigit(C)
  48.     #define ISXDIGIT(C)         isxdigit(C)
  49. #else
  50.     // In MPW there are two problems with using the <ctype> functions:
  51.     //  1. They produce the wrong results for arguments that are <0 or >127.
  52.     //    2. They reference global data which doesn't exist in a code resource.
  53.     // Therefore we define the following functions instead:
  54.     
  55.     static Boolean ISSPACE( register char c ) {
  56.         return c==' ' || c==0x0D || c==0x03 || c=='\t';        // '\r' is _not_ 0x0D in MPW C...
  57.     }
  58.     static Boolean ISGRAPH( char c ) {
  59.         return c<0 || c>32;
  60.     }
  61.     static Boolean ISDIGIT( char c ) {
  62.         return c>='0' && c<='9';
  63.     }
  64.     static Boolean ISXDIGIT( register char c ) {
  65.         return (c>='0' && c<='9') || (c>='A' && c<='F') || (c>='a' && c<='f');
  66.     }
  67. #endif
  68.  
  69. typedef unsigned char uchar;
  70.  
  71. typedef enum{                            /* Parser tokens, mnemonic for help in debugging */
  72.     tokERROR = '?',
  73.     tokEOF = '\0',
  74.     tokIDENT = 'A',
  75.     tokINTEGER = '0',
  76.     tokSTRING = '“',
  77.     tokHEXSTRING = '«',
  78.     tokCOLON = ':',
  79.     tokATSIGN = '@',
  80.     tokLPAREN = '(',    tokRPAREN = ')',
  81.     tokLBRACKET = '[',    tokRBRACKET = ']',
  82.     tokLBRACE = '{',    tokRBRACE = '}',
  83.     tokOPTIONAL = '~'                        // Precedes optional keyword -- JPA 7/14/92
  84. } Token;
  85.  
  86. #define LITERAL_TOKENS "“«,:@()[]{}~"        /* List of chars that are tokens or begin tokens */
  87.  
  88. #ifndef NO_SYNTAX_CODES
  89.   #include "AEBuildGlobals.h"
  90.   AEBuild_SyntaxErrType AEBuild_ErrCode;    /* Globals set when syntax error occurs */
  91.   long    AEBuild_ErrPos;
  92.   static OSErr syntaxError( AEBuild_SyntaxErrType errCode );
  93.   #define SYNTAX_ERROR(CODE) syntaxError(CODE)
  94. #else
  95.   #define SYNTAX_ERROR(CODE) (aeBuild_SyntaxErr)
  96. #endif
  97.  
  98.  
  99. static Token
  100.     peekToken( register const char **src ),
  101.     getToken( const char **src, long *val );
  102. static OSErr parseObj( AEStreamRef s, const char **src, va_list *args );
  103.  
  104.  
  105. #ifdef THINK_C
  106. #if !__option(macsbug_names)
  107.     // Always turn on macsbug names for the non-static functions.
  108.     #define NO_NAMES
  109.     #pragma options(macsbug_names)
  110. #endif
  111. #endif
  112.  
  113.  
  114. /* AE_BUILD  Construct an AppleEvent structure based on the format in src and the
  115.              following parameters (if any). */
  116. OSErr
  117. AEBuild( AEDesc *dst, const char *src, ... )
  118. {
  119.     va_list args;
  120.     OSErr err;
  121.     
  122.     va_start(args,src);
  123.     err= vAEBuild(dst,src,args);
  124.     va_end(args);
  125.     return err;
  126. }
  127.  
  128.  
  129. /* V_AE_BUILD  Same as AEBuild, but parameters are passed explicitly as a va_list.
  130.                This is analogous to vprintf. */
  131. OSErr
  132. vAEBuild( AEDesc *dst, const char *src, const void *args )
  133. {
  134.     OSErr err;
  135.     const char *pos = src;
  136.     AEStream s;
  137.     
  138. #ifndef NO_SYNTAX_CODES
  139.     AEBuild_ErrCode = aeBuildSyntaxNoErr;            /* Reset syntax-err code */
  140. #endif
  141.  
  142.     err= AEStream_Open(&s);
  143.     if( err==noErr )
  144.         err= parseObj(&s,&pos,(va_list*)&args);
  145.     if( err==noErr )
  146.         if( peekToken(&pos)!=tokEOF )                /* Extra stuff past end! */
  147.             err= SYNTAX_ERROR(aeBuildSyntaxNoEOF);
  148.     if( err ) {
  149.         AEStream_Close(&s,NULL);
  150.         dst->descriptorType = typeNull;
  151.         dst->dataHandle = NULL;
  152. #ifndef NO_SYNTAX_CODES
  153.         AEBuild_ErrPos = StripAddress((Ptr)pos)-StripAddress((Ptr)src);
  154. #endif
  155.     } else
  156.         err= AEStream_Close(&s,dst);
  157.     return err;
  158. }
  159.  
  160.  
  161. #ifdef NO_NAMES
  162.     #undef NO_NAMES
  163.     #pragma options(!macsbug_names)
  164. #endif
  165.  
  166.  
  167. /*******************************  THE UTILITY BELT  ************************************/
  168.  
  169.  
  170. /* WRITE_INT_DESC  Create a descriptor for a long or short integer */
  171. static OSErr
  172. writeIntDesc( AEStreamRef s, long n )
  173. {
  174.     if( n>32767 || n<-32767 )                    /* Pascal doesn't like 16-bit -32768 */
  175.         return AEStream_WriteDesc(s,'long',(Ptr)&n,4);
  176.     else
  177.         return AEStream_WriteDesc(s,'shor',(Ptr)&n+2,2);
  178. }
  179.  
  180.  
  181. /*******************************  THE TOKENIZER  ***************************************/
  182.  
  183.  
  184. /* IS_LITERAL_TOKEN  Determine whether the character c is one of the literal tokens */
  185. static Boolean
  186. isLiteralToken( register char c )
  187. {
  188.     register char *str = LITERAL_TOKENS;
  189.     register char cc;
  190.     
  191.     while( (cc= *str++) !='\0' )            /* Search through LITERAL_TOKENS string */
  192.         if( c==cc )
  193.             return true;
  194.     return false;
  195. }
  196.  
  197.  
  198. /* PEEK_TOKEN  Peek ahead at the next token. Doesn't retrieve value of ints/idents */
  199. static Token
  200. peekToken( register const char **src )
  201. {
  202.     register char c;
  203.     
  204.     do{
  205.         c= *(*src)++;                                /* Skip whitespace */
  206.     }while( ISSPACE(c) );
  207.     (*src)--;                                        /* Don't eat the non-whitespace char */
  208.     
  209.     if( c=='\0' )                                    /* Classify char */
  210.         c= tokEOF;
  211.     else if( ISDIGIT(c) || c=='-' )
  212.         c= tokINTEGER;
  213.     else if( isLiteralToken(c) )
  214.         ;
  215.     else if( ISGRAPH(c) )
  216.         c= tokIDENT;
  217.     else
  218.         c= tokERROR;
  219.     return (Token) c;                                /* Return it */
  220. }
  221.  
  222.  
  223. /* GET_TOKEN  Scan the next token out of the format string. Here's how val will be set:
  224.  *                tokINTEGER:    Integer value
  225.  *                tokIDENT:    4-char identifier name
  226.  *                tokSTRING:    Pointer to 1st char of string (*src pts to 1st char past '”')
  227.  *                tokHEXSTRING: Pointer to 1st char of hex data
  228.  *                other:        Undefined. */
  229. static Token
  230. getToken( const char **src, long *val )
  231. {
  232.     Token tok;
  233.     register const char *cp;
  234.     register char c;
  235.     
  236.     tok= peekToken(src);                            /* Peek at next char */
  237.     cp = *src;
  238.     c = *cp++;
  239.     
  240.     switch( tok ) {
  241.         case tokINTEGER: {                            /* Interpret number: */
  242.             Boolean minus = (c=='-');
  243.             
  244.             if( minus ) {
  245.                 c= *cp++;
  246.                 if( !ISDIGIT(c) ) {                        /* "-" not followed by digit! */
  247.                     SYNTAX_ERROR(aeBuildSyntaxBadNegative);
  248.                     tok= tokERROR;
  249.                     cp -= 2;
  250.                     goto exit;
  251.                 }
  252.             }
  253.             *val = 0;
  254.             do {
  255.                 *val= *val*10 + (c-'0');                /* Accumulate digits */
  256.                 c= *cp++;
  257.             } while( ISDIGIT(c) );
  258.             cp--;
  259.             if( minus )
  260.                 *val= -*val;
  261.             break;
  262.         }
  263.  
  264.         case tokERROR:                                /* Illegal character */
  265.             SYNTAX_ERROR(aeBuildSyntaxBadToken);
  266.             cp--;
  267.             break;
  268.             
  269.         case tokEOF:                                /* …or end of format string */
  270.             SYNTAX_ERROR(aeBuildSyntaxBadEOF);
  271.             cp--;
  272.             break;
  273.  
  274.         case tokIDENT: {
  275.             char endc;
  276.             
  277.             if( c=='\'' )
  278.                 endc = '\'';
  279.             else if( c=='‘' )
  280.                 endc = '’';
  281.             else
  282.                 endc = '\0';
  283.             
  284.             if( endc ) {                            /* Quoted identifier: */
  285.                 short count = 0;
  286.                 *val = '    ';
  287.                 do{
  288.                     c= *cp++;
  289.                     if( c==endc )
  290.                         break;
  291.                     if( c=='\\' )                        /* Backslash: take next char */
  292.                         c= *cp++;                        /* literally, unless it's EOF */
  293.                     if( c=='\0' )
  294.                         break;
  295.                     if( count<4 )
  296.                         ((char*)val)[count++] = c;        /* Copy 1st 4 chars to value */
  297.                 }while( true );
  298.                 if( c!=endc ) {
  299.                     SYNTAX_ERROR(aeBuildSyntaxMissingQuote);
  300.                     tok= tokERROR;                        /* Unexpected eof */
  301.                     goto exit;
  302.                 }
  303.             } else {                                /* Other identifier: */
  304.                 short count = 0;
  305.                 *val = '    ';
  306.                 do{
  307.                     if( count<4 )
  308.                         ((char*)val)[count++] = c;        /* Copy 1st 4 chars to value */
  309.                     c= *cp++;
  310.                 }while( ISGRAPH(c) && !isLiteralToken(c) );
  311.                 cp--;
  312.             }
  313.             break;
  314.         }
  315.         case tokSTRING:                                /* String: */
  316.             *val = (long)cp;                            /* Store 1st char posn in *val */
  317.             do{
  318.                 c= *cp++;
  319.             }while( c && c!='”' );                        /* Skip chars 'til end */
  320.             if( c=='\0' ) {
  321.                 SYNTAX_ERROR(aeBuildSyntaxNoCloseString);
  322.                 tok= tokERROR;
  323.             }
  324.             break;
  325.             
  326.         case tokHEXSTRING:                            /* Hex string: */
  327.             *val = (long)cp;                            /* Store 1st char posn in *val */
  328.             do{
  329.                 c= *cp++;
  330.             }while( ISXDIGIT(c) || ISSPACE(c) );        /* Skip chars 'til end */
  331.             if( c!='»' ) {
  332.                 SYNTAX_ERROR( c ?aeBuildSyntaxBadHex :aeBuildSyntaxNoCloseHex);
  333.                 tok= tokERROR;
  334.             }
  335.             break;
  336.     }
  337.     
  338. exit:
  339.     *src = cp;
  340.     return tok;
  341. }
  342.  
  343.  
  344. /* SYNTAX_ERROR  Report a syntax error (somehow) and return the appropriate error code */
  345. #ifndef NO_SYNTAX_CODES
  346. static OSErr
  347. syntaxError( AEBuild_SyntaxErrType errCode )
  348. {
  349.     if( AEBuild_ErrCode==aeBuildSyntaxNoErr )
  350.         AEBuild_ErrCode = errCode;
  351.     return aeBuild_SyntaxErr;
  352. }
  353. #endif
  354.  
  355.  
  356. /* WRITE_STRING_DESCRIPTOR  Write a descriptor for a just-parsed string */
  357. static OSErr
  358. writeStringDescriptor( long tokenVal, const char *src, DescType type, AEStreamRef s )
  359. {
  360.     return AEStream_WriteDesc(s, type,
  361.                               (void*)tokenVal,
  362.                               StripAddress((Ptr)src) -1 -StripAddress((Ptr)tokenVal));
  363. }
  364.  
  365.  
  366. /* WRITE_HEX_STRING_DESCRIPTOR  Write a descriptor for a just-parsed hex string */
  367. static OSErr
  368. writeHexStringDescriptor( long tokenVal, const char *src, DescType type, AEStreamRef s )
  369. {
  370.     long size;
  371.     uchar **h, *dst, *start,*end, *cp, c;
  372.     Boolean hi;
  373.     OSErr err;
  374.     
  375.     // Now that we have streams, we could just write the bytes one by one to the stream,
  376.     // but keeping our own buffer is faster since we're just writing to it via a pointer.
  377.     
  378.     end = (uchar*)StripAddress((Ptr)src) -1;
  379.     start = (uchar*)StripAddress((Ptr)tokenVal);
  380.     size = (end-1-start)/2;                            /* Max possible len */
  381.     
  382.     h = (uchar **)NewHandle(size);                        /* Create temp buffer for data */
  383.     if( MemError() ) return MemError();
  384.     
  385.     for( cp=(uchar*)tokenVal, hi=true, dst=*h; cp<end; cp++ ) {
  386.         c= *cp;                                        /* Scan source text: */
  387.         if( ISXDIGIT(c) ) {
  388.             if( ISDIGIT(c) )
  389.                 c -= '0';
  390.             else if( c>='a' )
  391.                 c -= 'a'-10;
  392.             else
  393.                 c -= 'A'-10;
  394.             if( hi )
  395.                 *dst = c<<4;                            /* Copy to destination, */
  396.             else                                        /* one nybble at a time */
  397.                 *dst++ |= c;
  398.             hi = !hi;
  399.         }
  400.     }
  401.     if( !hi ) {
  402.         DisposeHandle((Handle)h);                        /* Odd number of digits -- error */
  403.         return SYNTAX_ERROR(aeBuildSyntaxOddHex);
  404.     }
  405.     size= dst-*h;
  406.     SetHandleSize((Handle)h, size);                        /* Resize buffer to make room */
  407.     MoveHHi((Handle)h);
  408.     HLock((Handle)h);
  409.     
  410.     err= AEStream_WriteDesc(s, type, (Ptr)*h, size);    /* Build the descriptor */
  411.     DisposeHandle((Handle)h);
  412.     return err;
  413. }
  414.  
  415.  
  416. /*******************************  HEAVY_DUTY PARSING  **********************************/
  417.  
  418.  
  419. /* PARSE_DATA  Parse a single data item, the data of an AEDesc. Usually an integer. */
  420. /*               objType is the type to coerce to, or 0L */
  421. static OSErr
  422. parseData( AEStreamRef s, const char **src, va_list *args, DescType objType )
  423. {
  424.     Token t;
  425.     long val;
  426.     OSErr err;
  427.     
  428.     t= getToken(src, &val);
  429.  
  430.     if( t==')' ) {
  431.         if( objType==0L )
  432.             objType= typeNull;
  433.         return AEStream_WriteDesc(s,objType,NULL,0);
  434.  
  435.     } else if( t==tokINTEGER ) {                        /* Direct number: */
  436.         if( objType==0L || objType=='long' )
  437.             err= AEStream_WriteDesc(s,'long',&val,sizeof(long));    /* Longs are e-z */
  438.         else {
  439.             AEDesc desc;
  440.             err= AECoercePtr('long',(Ptr)&val,sizeof(val), objType,&desc);    /* Coerce!! */
  441.             if( !err ) {
  442.                 err= AEStream_WriteAEDesc(s,&desc);                /* Write resulting desc */
  443.                 AEDisposeDesc(&desc);
  444.             }
  445.         }
  446.     
  447.     } else if( t==tokIDENT )                            /* Identifier: */
  448.         err= AEStream_WriteDesc(s, objType?objType:'enum', &val, sizeof(val));
  449.     
  450.     else if( t==tokSTRING ) {                            /* String: */
  451.         if( !objType )
  452.             objType = 'TEXT';
  453.         err= writeStringDescriptor(val,*src, objType,s);
  454.     
  455.     } else if( t==tokHEXSTRING )                        /* Hex data: */
  456.         if( objType )
  457.             err= writeHexStringDescriptor(val,*src, objType,s);
  458.         else
  459.             err= SYNTAX_ERROR(aeBuildSyntaxUncoercedHex);
  460.  
  461.     else if( t=='@' )                                    /* Substitute fn argument: */
  462.         if( peekToken(src) == '@' ) {
  463.             long val;
  464.             AEDesc desc;                                        // @@ means use a handle
  465.             getToken(src,&val);
  466.             desc.descriptorType = objType;
  467.             desc.dataHandle = va_arg(*args,Handle);
  468.             err= AEStream_WriteAEDesc(s, &desc);
  469.             
  470.         } else {
  471.             short size;
  472.             
  473.             switch( objType ) {
  474.                 case 'shor':    size= sizeof(short); break;        /* Standard numeric types */
  475.                 case 'type':
  476.                 case 'enum':
  477.                 case 'long':    size= sizeof(long); break;
  478.                 case 'sing':    size= sizeof(float); break;
  479.                 case 'doub':    size= sizeof(double); break;    /**** MPW double, not THINK double!! ***/
  480.                 case 'exte':
  481. #if powerc
  482.                                 err = errAECoercionFail; goto done;
  483.                                 // There is no 'extended' type on PPC.
  484. #else
  485.                                 size= sizeof(extended); break;
  486. #endif
  487.                 case 'bool': {
  488.                     // Booleans have to be read as ints but written as 1 byte
  489.                     char b = va_arg(*args,int);
  490.                     if( b ) b=1;
  491.                     err= AEStream_WriteDesc(s,objType,&b,1);
  492.                     goto done;
  493.                 }
  494.                 case 'TEXT': {                                    /* TEXT: Get C string argument */
  495.                     char *str = va_arg(*args,char*);
  496.                     char *c;
  497.                     long len;
  498.                     
  499.                     for( c=str,len=0; *c; c++ )
  500.                         len++;
  501.                     err= AEStream_WriteDesc(s,'TEXT',str,len);
  502.                     goto done;
  503.                 }
  504.                 case 0L:                                        /* Get AEDesc* from param list */
  505.                     err= AEStream_WriteAEDesc(s, va_arg(*args,AEDesc*));
  506.                     goto done;
  507.                 default: {
  508.                     void *data;
  509.                     size = va_arg(*args,long);                /* Default: get size, then data ptr */
  510.                     data = va_arg(*args,void*);
  511.                     err= AEStream_WriteDesc(s,objType,data,size);
  512.                     goto done;
  513.                 }
  514.             }
  515.         
  516.             err= AEStream_WriteDesc(s,objType,*args,size);    /* Handle standard numeric types: */
  517.             *args = (Ptr)*args + size;                        /* Parameter data; assume numeric */
  518.                                                             /* (4 bytes by default) */
  519. done:        ;
  520.         }
  521.  
  522.     else
  523.         return SYNTAX_ERROR(aeBuildSyntaxBadData);
  524.  
  525.     if( !err && getToken(src,(long*)val) != ')' )        /* Must have closing paren */
  526.         err= SYNTAX_ERROR(aeBuildSyntaxNoCloseParen);
  527.     return err;
  528. }
  529.  
  530.  
  531. /* PARSE_LIST  Parse an AEDescList specifier. objType is the type to coerce to, or 0L */
  532. static OSErr
  533. parseList( AEStreamRef s, const char **src, va_list *args )
  534. {
  535.     Token t;
  536.     long val;
  537.     OSErr err= noErr;
  538.     Boolean first = true;
  539.     
  540.     err= AEStream_OpenList(s);
  541.     if( err )
  542.         return err;
  543.     
  544.     do{
  545.         t= peekToken(src);
  546.         if( t==tokERROR || t==tokEOF ) {
  547.             err= SYNTAX_ERROR(aeBuildSyntaxNoCloseBracket);
  548.             break;
  549.         } else if( t==']' ) {
  550.             getToken(src,&val);
  551.             break;                                    /* EXIT loop when "]" is found */
  552.         } else if( !first )
  553.             if( t==',' )                            /* Entries must be separated by "," */
  554.                 getToken(src,&val);
  555.             else {
  556.                 err= SYNTAX_ERROR(aeBuildSyntaxNoCloseBracket);
  557.                 break;
  558.             }
  559.         else
  560.             first = false;
  561.         
  562.         err= parseObj(s, src,args);
  563.     }while( !err );
  564.     
  565.     if( !err )
  566.         err= AEStream_CloseList(s);
  567.     return err;
  568. }
  569.  
  570.  
  571. /* PARSE_KEY_LIST  Parse an AERecord specifier. objType is the type to coerce to, or 0L */
  572. static OSErr
  573. parseKeyList( AEStreamRef s, const char **src, va_list *args, DescType objType )
  574. {
  575.     Token t;
  576.     DescType val,key;
  577.     OSErr err= noErr;
  578.     Boolean first = true;
  579.     
  580.     err= AEStream_OpenRecord(s,
  581.                              objType ?objType :typeAERecord);
  582.     if( err )
  583.         return err;
  584.     
  585.     do{
  586.         t= getToken(src,(long*)&key);    /* Get token; expecting keyword, "," or "}" */
  587.         if( t == '}' )
  588.             break;                        /* It's "}", so we're DONE */
  589.         else if( t==tokERROR || t==tokEOF ) {
  590.             err= SYNTAX_ERROR(aeBuildSyntaxNoCloseBrace);
  591.             break;
  592.         } else if( !first )
  593.             if( t==',' )                /* Entries after 1st are preceded by "," */
  594.                 t= getToken(src,(long*)&key);    /* Skip the ","; next token should be ident */
  595.             else {
  596.                 err= SYNTAX_ERROR(aeBuildSyntaxNoCloseBrace);
  597.                 break;
  598.             }
  599.         else
  600.             first = false;
  601.         
  602.         if( t != tokIDENT ) {
  603.             err= SYNTAX_ERROR(aeBuildSyntaxNoKey);        /* If not keyword, barf mightily */
  604.             break;
  605.         }
  606.         t= getToken(src,(long*)&val);            /* Get token, which must be a ":" */
  607.         if( t != ':' ) {
  608.             err= SYNTAX_ERROR(aeBuildSyntaxNoColon);
  609.             break;
  610.         }
  611.         
  612.         err= AEStream_WriteKey(s,key);                    /* Write key for descriptor */
  613.         if( !err )
  614.             err= parseObj(s, src,args);                    /* Parse/write object descriptor */
  615.     }while( !err );
  616.     
  617.     if( !err )
  618.         err= AEStream_CloseRecord(s);
  619.     return err;
  620. }
  621.  
  622.  
  623. /* PARSE_OBJ  Parse an object descriptor */
  624. static OSErr
  625. parseObj( AEStreamRef s, const char **src, va_list *args )
  626. {
  627.     Token t;
  628.     DescType val;
  629.     DescType objType = 0L;
  630.     OSErr err;
  631.     
  632.     t= getToken(src, (long*)&val);
  633.     
  634.     if( t==tokINTEGER )
  635.         return writeIntDesc(s,val);                    /* Create integer descriptor */
  636.  
  637.     else if( t==tokSTRING )                            /* Or string descriptor */
  638.         return writeStringDescriptor(val,*src, 'TEXT',s);
  639.     
  640.     else if( t==tokHEXSTRING )
  641.         return SYNTAX_ERROR(aeBuildSyntaxUncoercedHex);
  642.     
  643.     else if( t=='@' ) {                                /* Get AEDesc* from param list */
  644.         if( peekToken(src)=='@' )
  645.             return SYNTAX_ERROR(aeBuildSyntaxUncoercedDoubleAt);
  646.         else
  647.             return AEStream_WriteAEDesc(s,va_arg(*args,AEDesc*));
  648.  
  649.     } else if( t==tokIDENT ) {                        /* Identifier found: */
  650.         t= peekToken(src);
  651.         if( t=='(' || t=='[' || t=='{' ) {
  652.             objType = val;
  653.             t= getToken(src,(long*)&val);                    /* It's a coercion, go on */
  654.         } else
  655.             return AEStream_WriteDesc(s,'enum',&val,4);        /* Plain enum-code, return */
  656.     }
  657.     
  658.     if( t == '(' )
  659.         err= parseData(s, src,args, objType);        /* Get single data item */
  660.     else if( t == '[' )
  661.         if( objType==0L || objType==typeAEList )
  662.             err= parseList(s, src,args);            /* Get list of items */
  663.         else
  664.             err= SYNTAX_ERROR(aeBuildSyntaxCoercedList);
  665.     else if( t == '{' )
  666.         err= parseKeyList(s, src,args, objType);    /* Get keyword list (AERecord) */
  667.     else
  668.         err= SYNTAX_ERROR(aeBuildSyntaxBadDesc);
  669.     return err;
  670. }
  671.  
  672.  
  673. /* PARSE_PARAM  Parse desc and add as a parameter/attribute to an Apple event.  JPA 7/14/92 */
  674. static OSErr
  675. parseParam( AppleEvent *event, AEKeyword keyword, Boolean isAttribute, const char **src, va_list args )
  676. {
  677.     AEStream s;
  678.     AEDesc param;
  679.     OSErr err;
  680.     
  681.     err= AEStream_Open(&s);
  682.     if( err==noErr )
  683.         err= parseObj(&s,src,&args);
  684.     if( err )
  685.         AEStream_Close(&s,NULL);
  686.     else
  687.         err= AEStream_Close(&s,¶m);
  688.     if( !err ) {
  689.         if( isAttribute )
  690.             err= AEPutAttributeDesc(event,keyword,¶m);
  691.         else
  692.             err= AEPutParamDesc(event,keyword,¶m);
  693.         AEDisposeDesc(¶m);
  694.     }
  695.     return err;
  696. }
  697.  
  698.  
  699. /*******************************  BUILDING EVENTS  **********************************/
  700.  
  701. // JPA: Added 7/14/92
  702.  
  703.  
  704. #ifdef THINK_C
  705. #if !__option(macsbug_names)
  706.     #define NO_NAMES
  707.     #pragma options(macsbug_names)
  708. #endif
  709. #endif
  710.  
  711.  
  712. /* AEBUILD_APPLE_EVENT  Construct an Apple Event and read params */
  713. OSErr
  714. AEBuildAppleEvent(    AEEventClass theClass, AEEventID theID,
  715.                     DescType addressType, const void *addressData, long addressLength,
  716.                     short returnID, long transactionID, AppleEvent *result,
  717.                     const char *paramsFmt, ... )
  718. {
  719.     va_list args;
  720.     OSErr err;
  721.     
  722.     va_start(args,paramsFmt);
  723.     err= vAEBuildAppleEvent(theClass,theID,
  724.                             addressType,addressData,addressLength,
  725.                             returnID,transactionID,result,
  726.                             paramsFmt,
  727.                             args);
  728.     va_end(args);
  729.     return err;
  730. }
  731.  
  732.  
  733. /* V_AEBUILD_APPLE_EVENT  Construct an Apple Event and read params */
  734. OSErr
  735. vAEBuildAppleEvent(    AEEventClass theClass, AEEventID theID,
  736.                     DescType addressType, const void *addressData, long addressLength,
  737.                     short returnID, long transactionID, AppleEvent *resultEvt,
  738.                     const char *paramsFmt, const void *args )
  739. {
  740.     AEDesc addressDesc = {typeNull,NULL};
  741.     OSErr err;
  742.     
  743.     if( resultEvt==NULL ) return paramErr;
  744.     resultEvt->descriptorType = typeNull;
  745.     resultEvt->dataHandle = NULL;
  746.     
  747.     err= AECreateDesc(addressType,(Ptr)addressData,addressLength, &addressDesc);
  748.     if( err ) goto exit;
  749.     
  750.     err= AECreateAppleEvent(theClass,theID, &addressDesc,returnID,transactionID, resultEvt);
  751.     if( err ) goto exit;
  752.     
  753.     AEDisposeDesc(&addressDesc);
  754.     err= vAEBuildParameters(resultEvt,paramsFmt, args);
  755.     
  756. exit:
  757.     AEDisposeDesc(&addressDesc);
  758.     if( err )
  759.         AEDisposeDesc(resultEvt);
  760.     return err;
  761. }
  762.  
  763.  
  764. OSErr
  765. AEBuildParameters( AppleEvent *event, const char *format, ... )
  766. {
  767.     va_list args;
  768.     OSErr err;
  769.     
  770.     va_start(args,format);
  771.     err= vAEBuildParameters(event,format, args);
  772.     va_end(args);
  773.     return err;
  774. }
  775.  
  776.  
  777. OSErr
  778. vAEBuildParameters( AppleEvent *event, const char *format, const void *args )
  779. {
  780.     const char *pos = format;
  781.     AEStream s;
  782.     Token t;
  783.     DescType val,key;
  784.     OSErr err= noErr;
  785.     Boolean first = true;
  786.     
  787. #ifndef NO_SYNTAX_CODES
  788.     AEBuild_ErrCode = aeBuildSyntaxNoErr;        /* Reset syntax-err code */
  789. #endif
  790.  
  791.     err= AEStream_OpenEvent(&s, event);
  792.     if( err ) return err;
  793.     
  794.     // Now read all the parameters:
  795.  
  796.     do{
  797.         t= getToken(&pos,(long*)&key);            /* Get token; expecting keyword, "," or "}" */
  798.         if( t == tokEOF )
  799.             break;                                /* End of string, so we're DONE */
  800.         else if( t==tokERROR ) {
  801.             err= SYNTAX_ERROR(aeBuildSyntaxNoEOF);
  802.             break;
  803.         } else if( !first )
  804.             if( t==',' )                        /* Entries after 1st are preceded by "," */
  805.                 t= getToken(&pos,(long*)&key);    /* Skip the ","; next token should be ident */
  806.             else {
  807.                 err= SYNTAX_ERROR(aeBuildSyntaxNoEOF);
  808.                 break;
  809.             }
  810.         else
  811.             first = false;
  812.         
  813.         if( t == tokOPTIONAL ) {
  814.             t= getToken(&pos,(long*)&key);
  815.             err= AEStream_OptionalParam(&s,key);
  816.             if( err ) break;
  817.         }
  818.         
  819.         if( t != tokIDENT ) {
  820.             err= SYNTAX_ERROR(aeBuildSyntaxNoKey);        /* If not keyword, barf mightily */
  821.             break;
  822.         }
  823.         t= getToken(&pos,(long*)&val);                    /* Get token, which must be a ":" */
  824.         if( t != ':' ) {
  825.             err= SYNTAX_ERROR(aeBuildSyntaxNoColon);
  826.             break;
  827.         }
  828.         
  829.         err= AEStream_WriteKey(&s,key);                    /* Write key for parameter */
  830.         if( !err )
  831.             err= parseObj(&s, &pos,(va_list*)&args);    /* Parse/write parameter */
  832.     }while( !err );
  833.     
  834.     // Finally, finish and clean up:
  835.     
  836.     if( err )
  837.         AEStream_Close(&s,NULL);
  838.     else
  839.         err= AEStream_Close(&s, event);
  840.     
  841. #ifndef NO_SYNTAX_CODES
  842.     if( err )
  843.         AEBuild_ErrPos = StripAddress((Ptr)pos)-StripAddress((Ptr)format);
  844. #endif
  845.     
  846.     return err;
  847. }
  848.  
  849.  
  850. #ifdef NO_NAMES
  851.     #undef NO_NAMES
  852.     #pragma options(!macsbug_names)
  853. #endif
  854.